using System;
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using Random = UnityEngine.Random;

public class GameScript : MonoBehaviour {
	public int NumberOfUnits = 1;
    public int Width = 10;
    public int Length = 10;
	
	private int[,] _heightMap;
	private List<Unit> _units;
	
	private Unit _selectedUnit;
    private GameObject _selectedUnitTile;
    private GameObject _currentTile;
    private Color _previousColor;

    private List<GameObject> _availableTiles;
    public List<GameObject> _pathTiles;
	
	// Use this for initialization
	void Start () {
		var gridScript = (GridScript)GetComponent("GridScript");
        gridScript.Width = Width;
        gridScript.Length = Length;
		_heightMap = gridScript.Initialize();
        // Centering camera
        Camera.main.transform.position += new Vector3((Width / 2) - 1, 0, (Length / 2) - 1);
		PopulateUnits();
	}
	
	// Update is called once per frame
	void Update () {
        UpdateCurrentUnit();
        HighlightCurrentTile();
	}

    private void HighlightCurrentTile()
    {
        var mousePosition = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(mousePosition, out hit))
        {
            var go = hit.collider.gameObject;
            if (_currentTile != go && go.tag == "Terrain")
            {
                if (_currentTile != null)
                {
                    _currentTile.renderer.material.color = _previousColor;
                }
                _currentTile = go;
                _previousColor = _currentTile.renderer.material.color;
                _currentTile.renderer.material.color = Color.red;
            }

            // Calculating path from _selectedUnitTile to _currentTile
            if (_availableTiles.Contains(_currentTile))
            {
                if (_pathTiles != null)
                {
                    foreach (var pathTile in _pathTiles)
                    {
                        pathTile.renderer.material.color = Color.green;
                    }
                }

                _pathTiles = GetPath(_selectedUnitTile, 0);

                foreach (var pathTile in _pathTiles)
                {
                    pathTile.renderer.material.color = Color.red;
                }
            }
        }
    }

    private List<GameObject> GetPath(GameObject currentTile, int stepsTaken)
    {
        if (currentTile == _currentTile)
        {
            return new List<GameObject> { currentTile };
        }
        var x = (int)currentTile.transform.position.x;
        var y = (int)currentTile.transform.position.z;

        var path = new List<GameObject>();
        var newPath = new List<GameObject>();

        if (stepsTaken < _selectedUnit.MovementRange)
        {
            int nextHeight;
            GameObject nextTile;
            if (x > 0)
            {
                nextHeight = _heightMap[x - 1, y];
                nextTile = _availableTiles.FirstOrDefault(o => o.transform.position == new Vector3(x - 1, nextHeight, y));
                if (nextTile != null)
                {
                    path = GetPath(nextTile, stepsTaken + 1);
                }
            }
            if (x < Width - 1)
            {
                nextHeight = _heightMap[x + 1, y];
                nextTile = _availableTiles.FirstOrDefault(o => o.transform.position == new Vector3(x + 1, nextHeight, y));
                if (nextTile != null)
                {
                    newPath = GetPath(nextTile, stepsTaken + 1);
                }
                if (newPath.Count > 0 && newPath.Count < path.Count)
                {
                    path = newPath;
                }
            }
            if (y > 0)
            {
                nextHeight = _heightMap[x, y - 1];
                nextTile = _availableTiles.FirstOrDefault(o => o.transform.position == new Vector3(x, nextHeight, y - 1));
                if (nextTile != null)
                {
                    newPath = GetPath(nextTile, stepsTaken + 1);
                }
                if (newPath.Count > 0 && newPath.Count < path.Count)
                {
                    path = newPath;
                }
            }
            if (y < Length - 1)
            {
                nextHeight = _heightMap[x, y + 1];
                nextTile = _availableTiles.FirstOrDefault(o => o.transform.position == new Vector3(x, nextHeight, y + 1));
                if (nextTile != null)
                {
                    newPath = GetPath(nextTile, stepsTaken + 1);
                }
                if (newPath.Count > 0 && newPath.Count < path.Count)
                {
                    path = newPath;
                }
            }
        }
        if (path.Count > 0)
        {
            path.Insert(0, currentTile);
        }
        return path;
    }

    private void UpdateCurrentUnit()
    {
        if (_selectedUnit != GetNextUnit())
        {
            if (_selectedUnit != null)
            {
                _selectedUnit.gameObject.renderer.material.color = Color.blue;
            }
            _selectedUnit = GetNextUnit();
            _selectedUnit.gameObject.renderer.material.color = Color.green;

            // Calculate movement
            if (_availableTiles != null)
            {
                foreach (var tile in _availableTiles)
                {
                    tile.renderer.material.color = Color.gray;
                }
            }
            _availableTiles = new List<GameObject>();
            var unitTransform = _selectedUnit.gameObject.transform;
            var tiles = GameObject.FindGameObjectsWithTag("Terrain");
            HighlightMovement(tiles, (int) unitTransform.position.x, (int) unitTransform.position.z, 0);
        }
    }

    private void HighlightMovement(IEnumerable<GameObject> tiles, int x, int y, int stepsTaken)
    {
        var currentHeight = _heightMap[x, y];
        var currentTile = tiles.Single(o => o.transform.position == new Vector3(x, currentHeight, y));
        if (stepsTaken == 0)
        {
            _selectedUnitTile = currentTile;
        }
        currentTile.renderer.material.color = Color.green;
        _availableTiles.Add(currentTile);
        if (stepsTaken < _selectedUnit.MovementRange)
        {
            if (x > 0)
            {
                if (_heightMap[x - 1, y] - currentHeight <= _selectedUnit.JumpRange)
                {
                    HighlightMovement(tiles, x - 1, y, stepsTaken + 1);
                }
            }
            if (x < Width - 1)
            {
                if (_heightMap[x + 1, y] - currentHeight <= _selectedUnit.JumpRange)
                {
                    HighlightMovement(tiles, x + 1, y, stepsTaken + 1);
                }
            }
            if (y > 0)
            {
                if (_heightMap[x, y - 1] - currentHeight <= _selectedUnit.JumpRange)
                {
                    HighlightMovement(tiles, x, y - 1, stepsTaken + 1);
                }
            }
            if (y < Length - 1)
            {
                if (_heightMap[x, y + 1] - currentHeight <= _selectedUnit.JumpRange)
                {
                    HighlightMovement(tiles, x, y + 1, stepsTaken + 1);
                }
            }
        }
    }

	private void PopulateUnits()
	{
		_units = new List<Unit>();
		var unit = (GameObject) Resources.Load("Unit");
        unit.renderer.material.color = Color.blue;
		
		for (var unitCount = 0; unitCount < NumberOfUnits; unitCount++)
		{
			var i = Random.Range(0, Width);
			var k = Random.Range(0, Length);
			// unit height is 1.5
			var j = _heightMap[i, k] + 1f;
			
			_units.Add((Unit)((GameObject)Instantiate(unit, new Vector3(i, j, k), new Quaternion(0f, 0f, 0f, 0f))).GetComponent("Unit"));
		}
	}
	
	private Unit GetNextUnit()
	{
        _units[0].MovementRange = 3;
        _units[0].JumpRange = 1;
		return _units[0];
	}
}
